package cn.com.chengmandian.bike.service.impl;

import cn.com.chengmandian.bike.dto.MemberCardDto;
import cn.com.chengmandian.bike.mapper.EbMemberCardMapper;
import cn.com.chengmandian.bike.model.*;
import cn.com.chengmandian.bike.model.vo.MemberCardVo;
import cn.com.chengmandian.bike.service.*;
import cn.com.chengmandian.bike.utils.AliPayUtil;
import cn.com.chengmandian.bike.utils.WxPayUtil;
import cn.com.chengmandian.core.common.constants.CommonConstant;
import cn.com.chengmandian.core.redis.service.RedisService;
import cn.com.chengmandian.core.web.exceptions.BadResponseException;
import cn.com.chengmandian.core.web.pojo.AgentInfo;
import cn.com.chengmandian.core.web.pojo.AjaxResult;
import cn.com.chengmandian.core.web.utils.LocalMapUtil;
import cn.com.chengmandian.core.web.utils.SpringContextUtil;
import cn.com.chengmandian.service.bike.BikePayClient;
import cn.com.chengmandian.service.bike.constants.*;
import cn.com.chengmandian.service.bike.form.BikeRefundForm;
import cn.com.chengmandian.service.bike.form.RideCardRefundForm;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;

/**
 * <p>
 * 电单车运营平台骑行卡记录 服务实现类
 * </p>
 *
 * @author AutoGenerator
 * @since 2021-12-15
 */
@Service
public class EbMemberCardServiceImpl extends ServiceImpl<EbMemberCardMapper, EbMemberCard> implements IEbMemberCardService {
    @Autowired
    private EbMemberCardMapper memberCardMapper;

    @Autowired
    private IEbCardInfoService cardInfoService;

    @Autowired
    private IEbMemberPayService memberPayService;

    @Autowired
    private IEbRefundInfoService refundInfoService;

    @Autowired
    private IEbMemberWalletService memberWalletService;

    @Autowired
    private IEbMemberInfoService memberInfoService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private IEbMessageInfoService messageInfoService;

    @Autowired
    private IEbAgentInfoService agentInfoService;

    @Autowired
    private IEbPayAccountService payAccountService;

    @Autowired
    private BikePayClient bikePayClient;

    @Autowired
    private IEbMemberMoneyService memberMoneyService;

    @Override
    public MemberCardVo getMemberAvailableRecord(Integer areaId, Integer memberId) {
        return memberCardMapper.getMemberAvailable(areaId, memberId);
    }

    @Override
    public IPage<MemberCardVo> pages(MemberCardDto memberCardDto, Integer memberId) {
        return memberCardMapper.selectCardList(Page.of(memberCardDto.getPageNumber(), memberCardDto.getPageSize()), memberCardDto, memberId);
    }

    @Override
    public MemberCardVo detail(Integer id) {
        return baseMapper.detail(id);
    }

    public static void main(String[] args) {
        Date startTime = new Date();
        System.out.println(DateUtil.date().compareTo(DateUtil.offsetMonth(DateUtil.date(),1)));
    }

    @Override
    public EbMemberCard createCard(Integer payId,Integer memberId, Integer cardId, BigDecimal money, Integer sourceType, Integer sourceId) {
        EbCardInfo cardInfo = cardInfoService.getById(cardId);
        Date now = new Date();
        EbMemberCard memberCard = new EbMemberCard();
        memberCard.setAreaId(cardInfo.getAreaId());
        memberCard.setMemberId(memberId);
        memberCard.setCardId(cardId);
        memberCard.setMoney(money);
        memberCard.setStartTime(now);
        memberCard.setExpireTime(DateUtil.offsetDay(now, cardInfo.getDays()));
        memberCard.setRideTimes(cardInfo.getRideTimes());
        memberCard.setBalanceTimes(cardInfo.getRideTimes());
        if(cardInfo.getCouponMaxMoney() != null && cardInfo.getCouponMaxMoney().compareTo(BigDecimal.ZERO) > 0) {
            memberCard.setCouponMaxMoney(cardInfo.getCouponMaxMoney());
            memberCard.setCouponBalanceMoney(cardInfo.getCouponMaxMoney());
        }
        memberCard.setPayId(payId);
        memberCard.setPayStatus(CardPayStatusEnum.PAY.getCode());
        memberCard.setSourceType(sourceType);
        memberCard.setSourceId(sourceId);
        this.save(memberCard);
        return memberCard;
    }

    @Override
    public void costTimes(Integer cardRecordId, BigDecimal rideCardFreeMoney) {
        memberCardMapper.updateTimes(cardRecordId);

        // 更新剩余可减免费用
        EbMemberCard memberCard = this.getById(cardRecordId);
        EbCardInfo cardInfo = cardInfoService.getById(memberCard.getCardId());
        if(cardInfo != null && cardInfo.getCouponType().equals(CardInfoCouponTypeEnum.MONEY.getCode())){
            if(memberCard.getCouponMaxMoney() != null && memberCard.getCouponMaxMoney().compareTo(BigDecimal.ZERO) > 0
                    && rideCardFreeMoney != null && rideCardFreeMoney.compareTo(BigDecimal.ZERO) > 0){
                this.update(new LambdaUpdateWrapper<EbMemberCard>()
                        .set(EbMemberCard::getCouponBalanceMoney, memberCard.getCouponBalanceMoney().subtract(rideCardFreeMoney))
                        .eq(EbMemberCard::getId, memberCard.getId()));
            }
        }
    }

    @Override
    public void refund(RideCardRefundForm form) {
        String key = SpringContextUtil.getAppName().toUpperCase() + ":BIKE:REFUND:RIDE:CARD:" + form.getMemberCardId();
        try {
            if (redisService.hasKey(key)) {
                throw new BadResponseException("该订单正在退款中,请稍等....");
            }
            redisService.set(key, form.getMemberCardId(), 3);
            // 骑行卡信息
            EbMemberCard ebMemberCard = this.getById(form.getMemberCardId());
            Assert.notNull(ebMemberCard, "用户购买的骑行卡不存在！");
            Assert.notNull(ebMemberCard.getPayId(), "选择的骑行卡购买信息不支持退款！");
            Assert.isTrue(ebMemberCard.getBalanceTimes() > 0, "骑行卡次数已用完！");
            Date expireTimeLong = DateUtil.offsetMonth(ebMemberCard.getExpireTime(), 1);
            Assert.isTrue(DateUtil.date().compareTo(expireTimeLong) < 0, "骑行卡过期时间过长不支持退款！");

            // 支付记录
            EbMemberPay ebMemberPay = memberPayService.getById(ebMemberCard.getPayId());
            Assert.notNull(ebMemberPay, "支付记录信息不存在");
            Assert.isTrue(ebMemberPay.getMoney().compareTo(BigDecimal.ZERO) > 0, "金额不支持退款!");
            Assert.isTrue(form.getRefundMoney().compareTo((ebMemberPay.getMoney().subtract(ObjectUtil.isNull(ebMemberPay.getRefundMoney()) ? BigDecimal.ZERO : ebMemberPay.getRefundMoney()))) <= 0,"退款金额超出");

            AgentInfo agentInfo = LocalMapUtil.getAgentInfo();
            String notifyUrl = "http://"+ agentInfo.getDomain() + "/api/v1/bikeuser/notify";
            if (ebMemberPay.getPayChannel().equals(PayChannelEnum.WALLET.getCode())) {
                // 生成退款支付记录
                EbRefundInfo ebRefundInfo = refundInfoService.createRefund(ebMemberPay.getAreaId(),ebMemberPay.getMemberId(), ebMemberPay.getId(), ebMemberPay.getId(), ebMemberPay.getFeeRatio(), form.getRefundMoney(), RefundBusinessTypeEnum.CARD, RefundChannelEnum.MONEY, form.getRefundReason(),form.getLoginUser());
                memberWalletService.refundMoney(ebMemberCard.getAreaId(), ebMemberPay.getMemberId(), MemberMoneyBusinessTypeEnum.RIDE_CARD_REFUND, ebMemberCard.getId(), form.getRefundMoney(),BigDecimal.ZERO);
                this.rideCardRefundSuccess(ebRefundInfo, ebMemberPay.getPayNo());
            } else if (ebMemberPay.getPayChannel().equals(PayChannelEnum.WXPAY.getCode())) {
                // 生成退款支付记录
                EbRefundInfo ebRefundInfo = refundInfoService.createRefund(ebMemberPay.getAreaId(),ebMemberPay.getMemberId(), ebMemberPay.getId(), ebMemberPay.getId(), ebMemberPay.getFeeRatio(), form.getRefundMoney(), RefundBusinessTypeEnum.CARD, RefundChannelEnum.WX, form.getRefundReason(),form.getLoginUser());
                EbMemberInfo ebMemberInfo = memberInfoService.getById(ebMemberCard.getMemberId());
                EbAgentInfo info = agentInfoService.getById(agentInfo.getId());
                if (info.getPayAccountId() != null) {
                    EbPayAccount payAccount = payAccountService.getPayAccount(info.getPayAccountId(), ebMemberCard.getMemberId());
                    if (payAccount != null) {
                        BikeRefundForm refundForm = new BikeRefundForm();
                        refundForm.setBusinessType(RefundBusinessTypeEnum.CARD);
                        refundForm.setMemberPayId(ebMemberPay.getId());
                        refundForm.setMoney(ebRefundInfo.getMoney());
                        refundForm.setRefundNo(ebRefundInfo.getRefundNo());
                        refundForm.setAlipayUserId(ebMemberInfo.getAlipayUserId());
                        refundForm.setAlipayOpenId(ebMemberInfo.getAlipayOpenId());
                        AjaxResult result = bikePayClient.refund(ebMemberPay.getAgentId(), refundForm);
                        Assert.isTrue(result.isSuccess(), result.getMessage());
                        return;
                    }
                }
                try {
                    WxPayUtil.refund(
                            agentInfo.getWxMchId(),
                            ebRefundInfo.getRefundNo(),
                            ebMemberPay.getPayNo(),
                            "骑行卡订单退款",
                            ebRefundInfo.getMoney(),
                            ebMemberPay.getMoney(),
                            agentInfo.getWxKeyPath(),
                            agentInfo.getWxCertPath(),
                            notifyUrl + "/wx/ride/card/refund/" + LocalMapUtil.getAgentInfo().getId());
                } catch (Exception ex) {
                    log.error("微信退款失败", ex);
                    throw new BadResponseException("微信退款失败:" + ex.getMessage());
                }
            } else if (ebMemberPay.getPayChannel().equals(PayChannelEnum.ALIPAY.getCode())) {
                // 生成退款支付记录
                EbRefundInfo ebRefundInfo = refundInfoService.createRefund(ebMemberPay.getAreaId(),ebMemberPay.getMemberId(), ebMemberPay.getId(), ebMemberPay.getId(), ebMemberPay.getFeeRatio(), form.getRefundMoney(), RefundBusinessTypeEnum.CARD, RefundChannelEnum.ALI, form.getRefundReason(),form.getLoginUser());
                EbMemberInfo ebMemberInfo = memberInfoService.getById(ebMemberCard.getMemberId());
                EbAgentInfo info = agentInfoService.getById(agentInfo.getId());
                if (info.getPayAccountId() != null) {
                    EbPayAccount payAccount = payAccountService.getPayAccount(info.getPayAccountId(), ebMemberCard.getMemberId());
                    if (payAccount != null) {
                        BikeRefundForm refundForm = new BikeRefundForm();
                        refundForm.setBusinessType(RefundBusinessTypeEnum.CARD);
                        refundForm.setMemberPayId(ebMemberPay.getId());
                        refundForm.setMoney(ebRefundInfo.getMoney());
                        refundForm.setRefundNo(ebRefundInfo.getRefundNo());
                        refundForm.setAlipayUserId(ebMemberInfo.getAlipayUserId());
                        refundForm.setAlipayOpenId(ebMemberInfo.getAlipayOpenId());
                        AjaxResult result = bikePayClient.refund(ebMemberPay.getAgentId(), refundForm);
                        Assert.isTrue(result.isSuccess(), result.getMessage());
                        return;
                    }
                }
                try {
                    Map<String, String> res = AliPayUtil.getTradeRefundRequestParam(
                            agentInfo.getAliMiniAppid(),
                            ebMemberInfo.getAlipayUserId(),
                            ebMemberInfo.getAlipayOpenId(),
                            "骑行卡订单退款",
                            ebMemberPay.getPayNo(),
                            ebRefundInfo.getRefundNo(),
                            ebRefundInfo.getMoney(),
                            agentInfo.getAliPayPrivateKey(),
                            agentInfo.getAliPayCertPath(),
                            agentInfo.getAliPayPublicCertPath(),
                            agentInfo.getAliPayRootCertPath());
                    ebRefundInfo.setChannelNo(res.get("trade_no"));
                    this.rideCardRefundSuccess(ebRefundInfo, ebMemberPay.getPayNo());
                } catch (Exception ex) {
                    log.error("支付宝退款失败", ex);
                    throw new BadResponseException("支付宝退款失败:" + ex.getMessage());
                }
            }
        }catch (Exception ex){
            redisService.del(key);
            throw new BadResponseException(ex.getMessage());
        }
    }

    @Override
    public void rideCardRefundSuccess(EbRefundInfo ebRefundInfo, String payNo) {
        Assert.isTrue(ebRefundInfo.getBusinessType().equals(RefundBusinessTypeEnum.CARD.getCode()),"退款类型错误");
        // 支付记录
        EbMemberPay ebMemberPay = memberPayService.getByNo(payNo);
        Assert.notNull(ebMemberPay,"支付记录不存在！");

        // 骑行卡信息
        EbMemberCard ebMemberCard = this.getOne(new LambdaQueryWrapper<EbMemberCard>()
                .eq(EbMemberCard::getPayId,ebMemberPay.getId())
                .last("limit 0,1"));
        Assert.notNull(ebMemberCard,"骑行卡信息不存在！");

        this.update(new LambdaUpdateWrapper<EbMemberCard>()
                .set(EbMemberCard::getPayStatus,CardPayStatusEnum.REFUND.getCode())
                .eq(EbMemberCard::getId,ebMemberCard.getId()));

        BigDecimal refundMoney = (ObjectUtil.isNull(ebMemberPay.getRefundMoney()) ? BigDecimal.ZERO : ebMemberPay.getRefundMoney()).add(ebRefundInfo.getMoney());
        MemberPayStatusEnum memberPayStatusEnum = MemberPayStatusEnum.PAID;
        if(refundMoney.compareTo(ebMemberPay.getMoney()) >=0){
            memberPayStatusEnum = MemberPayStatusEnum.REFUND;
        }
        memberPayService.update(new LambdaUpdateWrapper<EbMemberPay>()
                .set(EbMemberPay::getStatus,memberPayStatusEnum.getCode())
                .set(EbMemberPay::getRefundMoney,refundMoney)
                .set(EbMemberPay::getRefundTime,new Date())
                .eq(EbMemberPay::getId,ebMemberPay.getId()));

        // 退款信息
        refundInfoService.update(new LambdaUpdateWrapper<EbRefundInfo>()
                .set(EbRefundInfo::getStatus,RefundStatusEnum.REFUND_OK.getCode())
                .set(EbRefundInfo::getCompleteTime,new Date())
                .set(EbRefundInfo::getRefundMoney,ebRefundInfo.getMoney())
                .set(EbRefundInfo::getChannelNo,ebRefundInfo.getChannelNo())
                .eq(EbRefundInfo::getId,ebRefundInfo.getId()));

        String key = SpringContextUtil.getAppName().toUpperCase() + ":BIKE:REFUND:RIDE:CARD:" + ebMemberCard.getId();
        redisService.del(key);

        messageInfoService.addSys(ebMemberPay.getMemberId(), MessageChannelEnum.SYS,
                "尊敬的用户，您于 "  + DateUtil.now() + " 退款成功" + ebRefundInfo.getMoney().doubleValue() + "元，感谢您的支持与信任！",
                null, MessageBusinessTypeEnum.CARD, null);

        // 非余额支付生成流水
        if(!PayChannelEnum.WALLET.getCode().equals(ebMemberPay.getPayChannel())) {
            memberMoneyService.createPay(ebMemberPay.getAreaId(), ebMemberPay.getMemberId(), MemberMoneyBusinessTypeEnum.RIDE_CARD_REFUND, ebMemberPay.getBusinessId(), MemberMoneyOperationTypeEnum.SERVICE, ebRefundInfo.getMoney());
        }
    }

    @Override
    public EbMemberCard getByRedeemCode(Integer memberId, Integer redeemCodeId) {
        return this.getOne(new LambdaQueryWrapper<EbMemberCard>()
                .eq(EbMemberCard::getSourceType, CommonConstant.TWO)
                .eq(EbMemberCard::getMemberId, memberId)
                .eq(EbMemberCard::getSourceId, redeemCodeId)
                .orderByDesc(EbMemberCard::getId)
                .last("limit 0,1"));
    }

    @Override
    public void advanceGiveCard(Integer memberId, String mobile) {
        if(StrUtil.isBlank(mobile)) return;

        this.update(new LambdaUpdateWrapper<EbMemberCard>()
                .set(EbMemberCard::getMemberId, memberId)
                .eq(EbMemberCard::getRegisterMemberPhone, mobile)
                .isNull(EbMemberCard::getMemberId));
    }
}
